#include <thread>
#include "av_player.h"
#include "av_recorder.h"
#include "log_print.h"
#include "napi/native_api.h"

constexpr size_t ARGC_MAX = 6;
constexpr int32_t STR_MAX_LENGTH = 4096;
constexpr size_t STR_TAIL_LENGTH = 1;

napi_status GetString(napi_env env, napi_value in, std::string& out)
{
    napi_valuetype type = napi_undefined;
    napi_status status = napi_typeof(env, in, &type);
    if(status != napi_ok || type != napi_string) {
        return status;
    }
    size_t maxLen = STR_MAX_LENGTH;
    status = napi_get_value_string_utf8(env, in, NULL, 0, &maxLen);
    if (maxLen <= 0) {
        return status;
    }
    LOGI("napi_value -> std::string get length %{public}d", (int)maxLen);
    char* buf = new (std::nothrow) char[maxLen + STR_TAIL_LENGTH];
    if (buf != nullptr) {
        size_t len = 0;
        status = napi_get_value_string_utf8(env, in, buf, maxLen + STR_TAIL_LENGTH, &len);
        if (status == napi_ok) {
            buf[len] = 0;
            out = std::string(buf);
        }
        delete[] buf;
    } else {
        status = napi_generic_failure;
    }
    return status;
}

static napi_value Play(napi_env env, napi_callback_info info)
{
    size_t argc = ARGC_MAX;
    napi_value argv[ARGC_MAX] = { nullptr };
    napi_status status = napi_get_cb_info(env, info, &argc, argv , nullptr, nullptr);
    if (status != napi_ok || argc < 2) {
        LOGE("invalid args.");
        return nullptr;
    }
    int32_t fd;
    status = napi_get_value_int32(env, argv[0], &fd);
    if (status != napi_ok) {
        return nullptr;
    }
    int64_t size;
    status = napi_get_value_int64(env, argv[1], &size);
    if (status != napi_ok) {
        return nullptr;
    }
    std::thread th([fd, size]() {
        AvPlayer::GetInstance().Play(fd, size);
    });
    th.detach();
    return nullptr;
}

static napi_value PrepareRecord(napi_env env, napi_callback_info info)
{
    size_t argc = ARGC_MAX;
    napi_value argv[ARGC_MAX] = { nullptr };
    napi_status status = napi_get_cb_info(env, info, &argc, argv , nullptr, nullptr);
    if (status != napi_ok || argc < 2) {
        LOGE("invalid args.");
        return nullptr;
    }
    std::string previewId;
    status = GetString(env, argv[0], previewId);
    if (status != napi_ok) {
        return nullptr;
    }
    std::string videoId;
    status = GetString(env, argv[1], videoId);
    if (status != napi_ok) {
        return nullptr;
    }
    AvRecorder::GetInstance().PrepareRecorder(previewId, videoId);
    return nullptr;
}

static napi_value StartRecord(napi_env env, napi_callback_info info)
{
    AvRecorder::GetInstance().StartRecorder();
    return nullptr;
}

static napi_value StopRecord(napi_env env, napi_callback_info info)
{
    AvRecorder::GetInstance().StopRecorder();
    return nullptr;
}

static napi_value ReleaseRecord(napi_env env, napi_callback_info info)
{
    AvRecorder::GetInstance().ReleaseRecorder();
    return nullptr;
}

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
    napi_property_descriptor desc[] = {
        {"play", nullptr, Play, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "prepareRecord", nullptr, PrepareRecord, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "startRecord", nullptr, StartRecord, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "stopRecord", nullptr, StopRecord, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "releaseRecord", nullptr, ReleaseRecord, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    AvPlayer::GetInstance().Export(env, exports);
    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "entry",
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
{
    napi_module_register(&demoModule);
}
